#include "os_task.h"
#include "os_mem.h"
#include "os_timer.h"
#include "os_mutex.h"
#include "os_sched.h"
#include <string.h>

#if OS_TIMER_ENABLE

#if !OS_TIMER_DBG_PRINT_ENABLE
#undef os_dbg
#define os_dbg(fmt, ...) do {}while (0)
#endif

static os_list_t timer_list;                    // 定时器列表
static os_mutex_t timer_mutex;                  // 操作定时器列表的互斥信号量
static os_event_t timer_list_event;             // 是否有定时器的事件控制
static uint8_t timer_task_stack[OS_TIMER_TASK_STACK_SIZE];

/**
 * 初始化定时器列表
 */
static void timer_list_init (void) {
    os_list_init(&timer_list, os_timer_t, item);
    os_mutex_init(&timer_mutex);
    os_event_init(&timer_list_event, OS_EVENT_TYPE_TIMER, 0);
}

/**
 * 列表保护，防止定时器任务和普通任务同时操作链表
 */
static void timer_list_begin_protect (void) {
        // 只有系统运行起来之后，才调用进行保护
        // 因为有可能在系统未起来之前创建删除定时器
        os_mutex_lock(&timer_mutex, 0);
}

/**
 * 退出对定时器列表的保护
 */
static void timer_list_end_protect (void) {
        os_mutex_unlock(&timer_mutex);
}

/**
 * @brief 显示定时器列表信息
 */
#if OS_TIMER_DBG_PRINT_ENABLE
static void timer_list_show(void) {
    os_dbg("--------------- timer list ---------------\n");

    int index = 0;
    os_list_for_each(timer, os_timer_t, &timer_list) {
        os_dbg("%d: %s, period = %d, curr: %d ms, reload: %d ms\n",
            index++, timer->name,
            (timer->state & OS_TIMER_ONE_SHOT) ? 0 : 1,
            timer->curr_ms,
            timer->reload_ms);
    }
    os_dbg("---------------- timer list end ------------\n");
}
#else
#define timer_list_show()
#endif

/**
 * 初始化定时器
 * @param timer 定时器
 * @param delay_ms 延时启动的毫秒数
 * @param tmo_ms 超时时间间隔
 * @param func 定时回调函数
 * @param arg 回调函数的参数
*/
static os_err_t timer_init (os_timer_t * timer, const char * name, int tmo_ms, os_timer_func_t func, void * arg, int flags) {
    strncpy(timer->name, name, OS_TIMER_NAME_SIZE);
    timer->name[OS_TIMER_NAME_SIZE - 1] = '\0';

    timer->reload_ms = tmo_ms;
    timer->curr_ms = timer->reload_ms;
    timer->func = func;
    timer->arg = arg;
    timer->state = flags;
    os_list_item_init(&timer->item, os_timer_t, item);
    return OS_ERR_OK;
}

/**
 * 初始化一个定时器
 * @param timer 定时器
 * @param tmo_ms 超时时间间隔
 * @param func 定时回调函数
 * @param arg 回调函数的参数
 * @param flags 相关标志
 */
os_err_t os_timer_init (os_timer_t * timer, const char * name, int tmo_ms, os_timer_func_t func, void * arg, int flags) {
    os_param_failed(timer == OS_NULL, OS_ERR_PARAM);
    os_param_failed(tmo_ms <= 0, OS_ERR_PARAM);  // 允许其中一个为0
    os_param_failed(func == OS_NULL, OS_ERR_PARAM);

    return timer_init(timer, name, tmo_ms, func, arg, flags);
}

/**
 * 取消定时器的初始化
 * @param timer 定时器
*/
os_err_t os_timer_uninit(os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, OS_ERR_PARAM);

    timer_list_begin_protect();

    // 定时器需要先关闭
    if ((timer->state & OS_TIMER_STARTED) != 0) {
        os_dbg("timer %s is not stopped", timer->name);

        timer_list_end_protect();
        return OS_ERR_STATE;
    }

    timer_list_end_protect();
    return OS_ERR_OK;
}

#if OS_MEM_ENABLE
/**
 * 创建一个定时器
 * @param timer 定时器
 * @param delay_ms 延时启动的毫秒数
 * @param tmo_ms 超时时间间隔
 * @param func 定时回调函数
 * @param arg 回调函数的参数
 * @param flags 相关标志
 * @return 创建的定时器
 */
os_timer_t * os_timer_create (const char * name, int tmo_ms, os_timer_func_t func, void * arg, int flags) {
    os_param_failed(tmo_ms <= 0, OS_NULL);  // 允许其中一个为0
    os_param_failed(func == OS_NULL, OS_NULL);

    // 分配内存
    os_timer_t * timer = os_mem_malloc(sizeof(os_timer_t));
    if (timer == OS_NULL) {
        os_dbg("error: alloc sem failed");
        return OS_NULL;
    }

    // 初始化信号量结构
    os_err_t err = timer_init(timer, name, tmo_ms, func, arg, flags | OS_FLAG_MEM_HEAP);
    if (err < 0) {
        os_dbg("error: init sem failed.");
        os_mem_free(timer);
        return OS_NULL;
    }

    return timer;
}

/**
 * 销毁一个定时器
 * @param 需要释放的信号量
 */
os_err_t os_timer_free (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, OS_ERR_PARAM);

    os_err_t err = os_timer_uninit(timer);
    if (err < 0) {
        os_dbg("timer free error.");
        return err;
    }

    // 定时器已经从链表中移除
    if (timer->state & OS_FLAG_MEM_HEAP) {
        os_mem_free(timer);
    }
    return OS_ERR_OK;
}
#endif

#if OS_TIMER_INFO_ENABLE

/**
 * 获取定时器还有多长时间超时
 * @param timer 查询的定时器
*/
int os_timer_left (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, 0);

    timer_list_begin_protect();
    int ms = timer->reload_ms;
    timer_list_end_protect();
    return ms;
}

/**
 * 检查定时器是否已经启动
 * @param timer 查询的定时器
*/
int os_timer_stopped (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, 0);

    timer_list_begin_protect();
    int state = timer->state;
    timer_list_end_protect();
    return (state & OS_TIMER_STARTED) == 0;
}

/**
 * 判断定时器是否在运行
 */
int os_timer_started (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, 0);

    timer_list_begin_protect();
    int started = timer->state & OS_TIMER_STARTED;
    timer_list_end_protect();
    return started;
}

#endif
/**
 * 将定时器插入到队列中
*/
static void insert_timer (os_timer_t * timer) {
    os_timer_t * pre = OS_NULL;
    os_list_for_each(curr, os_timer_t, &timer_list) {
        // 待插入的结点超时比当前结点超时大，应当继续往后寻找
        // 因此，先将自己的时间减一下，然后继续往下遍历
        if (timer->curr_ms > curr->curr_ms) {
            timer->curr_ms -= curr->curr_ms;
        } else if (timer->curr_ms == curr->curr_ms) {
            // 相等，插入到其之后，超时调整为0，即超时相等
            timer->curr_ms = 0;
            os_list_insert_after(&timer_list, curr, timer);
            break;
        } else {
            // 比当前超时短，插入到当前之前，那么当前的超时时间要减一下
            curr->curr_ms -= timer->curr_ms;
            if (pre) {
                os_list_insert_after(&timer_list, pre, timer);
            } else {
                os_list_insert_first(&timer_list, timer);
            }
            break;
        }
        pre = curr;
    }

    // 找不到合适的位置，即超时比所有的都长，插入到最后
    if (curr == OS_NULL) {
        os_list_insert_last(&timer_list, timer);
    }
}

/**
 * 启动定时器
 * @param timer 待启动的定时器
 */
os_err_t os_timer_start (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, OS_ERR_PARAM);

    // 正在运行时，不允许重新启动
    timer_list_begin_protect();
    if (timer->state & OS_TIMER_STARTED) {
        os_dbg("timer %s is already started.", timer->name);

        timer_list_end_protect();
        return OS_ERR_STATE;
    }

    // 重新加载一遍，以允许停止后重启
    timer->curr_ms = timer->reload_ms;

    // 标记为已启动状态并插入队列中
    timer->state |= OS_TIMER_STARTED;
    insert_timer(timer);

    timer_list_show();
    timer_list_end_protect();

    // 通知定时器任务当前有新的定时器到达
    os_int_status_t status = os_sched_int_lock();
    os_event_notify(&timer_list_event);
    os_sched_int_unlock(status);

    os_sched_run();
    return OS_ERR_OK;
}

/**
 * 终止定时器
 * @param timer 待终止的定时器
 */
os_err_t os_timer_stop (os_timer_t * timer) {
    os_param_failed(timer == OS_NULL, OS_ERR_PARAM);

    // 要求已经是运行的状态
    timer_list_begin_protect();
    if ((timer->state & OS_TIMER_STARTED) == 0) {
        os_dbg("timer %s is already stopped", timer->name);

        timer_list_end_protect();
        return OS_ERR_STATE;
    }

    // 当前任务如果还在队列(tmo队列和普通队列)中，则移除
    if ((timer->state & OS_TIMER_RUNNING) == 0) {
        // 将自己剩余的延时，加到后续的定时器上
        os_timer_t * next_timer = os_list_item_next(&timer_list, timer);
        if (next_timer) {
            next_timer->curr_ms += timer->curr_ms;
        }

        os_list_remove_item(&timer_list, timer);
    }

    timer->state &= ~OS_TIMER_STARTED;

    timer_list_show();
    timer_list_end_protect();
    return OS_ERR_OK;
}

/**
 * 遍历指定的定时器列表，调用各个定时器处理函数
 * 以前是通过定时器节拍发信号，但是这个效率太低了
 */
static void timer_task_entry (void * arg) {
    // 重载定时器列表
    os_list_t tmo_list;

    for (;;) {
        // 取第一个定时器的时间，然后进行延时
        os_int_status_t status = os_sched_int_lock();
        os_timer_t * timer = (os_timer_t *)os_list_first(&timer_list);
        if (timer == OS_NULL) {
            // 挂起等待可一个有效的定时器
            os_event_wait(&timer_list_event, OS_NULL, 0);
            os_sched_run();

            os_sched_int_unlock(status);
            continue;
        }
        os_sched_int_unlock(status);

        // 找到有效的定时器，根据其延时多少，将自己挂起来
        int tick_count = os_tick_count();
        status = os_sched_int_lock();
        os_event_wait(&timer_list_event, OS_NULL, timer->curr_ms);
        os_sched_int_unlock(status);
        os_sched_run();

        // 初始化超时队列和计算当前实际过去了多少时间
        // 不一定延时了timer->curr_ms时间，有可能因提前插入了定时器而唤醒
        os_list_init(&tmo_list, os_timer_t, item);
        tick_count = os_tick_count() - tick_count;

        // 遍历列表，看看是否有超时事件
        // 注意，之前的列表头任务可能已经被移除掉了，所以这里重新检查一下
        timer_list_begin_protect();
        timer = (os_timer_t *)os_list_first(&timer_list);
        int diff_ms = tick_count * OS_SYSTICK_MS;
        while (timer) {
            // 定时时间未到，结束扫描
            if (timer->curr_ms > diff_ms) {
                timer->curr_ms -= diff_ms;
                break;
            }

            // 定时时间到达，并且甚至有可能有时间更长
            // 预先取下一个，因为后面有调整结点的插入，链接关系处理不同，有可能导致整个遍历死循环
            os_timer_t * next_timer = os_list_item_next(&timer_list, timer);
            diff_ms -= timer->curr_ms;
            timer->curr_ms = 0;

            // 当前定时器肯定超时了，暂不调用回调函数，而是直接加入到等待队列中
            os_list_remove_item(&timer_list, timer);
            os_list_insert_last(&tmo_list, timer);

            os_dbg("timer %s tmo, diff: %d, curr: %d, reload: %d\n",
                        timer->name, diff_ms, timer->curr_ms, timer->reload_ms);

            // 继续搜索下一结点
            timer = next_timer;
        }
        // 到这里，上面的diff_ms可能是不为0的，所以可以发现该定时方法并不准

        // 执行定时函数，如果定时器需要重载，则重新插入链表
        while ((timer = os_list_remove_first(&tmo_list)) != OS_NULL) {
            // 执行调用，注意标记一下
            timer->state |= OS_TIMER_RUNNING;
            timer->func(timer, timer->arg);
            timer->state &= ~OS_TIMER_RUNNING;

            // 回调函数中，可能会停止定时器函数
            if (timer->state & OS_TIMER_STARTED) {
                // 重载定时器，先加入到等待插入的链表，避免破解现有的遍历
                if ((timer->state & OS_TIMER_ONE_SHOT) == 0) {
                    timer->curr_ms = timer->reload_ms;

                    insert_timer(timer);
                    timer_list_show();
                } else {
                    // 停止定时器
                    timer->state &= ~OS_TIMER_STARTED;
                }
            }
        }

        timer_list_end_protect();
    }
}
/**
 * 初始化定时器模块
 */
os_err_t os_timer_service_init (void) {
    // 定时器链表初始化
    timer_list_init();

    os_err_t err = os_task_init(&os_core.timer_task,
            "timer",
            timer_task_entry,
            OS_NULL,
            OS_TIMER_TASK_PRIO,
            timer_task_stack,
            sizeof(timer_task_stack));
    if (err < 0) {
        os_dbg("timer task init error");
        return err;
    }

    os_task_start(&os_core.timer_task);
    return OS_ERR_OK;
}

#endif // OS_TIMER_ENABLE
